﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace SG
{
    public class ICommonObject { };
    public class EventParam
    {
        public EventParam()
        {

        }
        public virtual EventParam Clone()
        {
            return new EventParam();
        }
        public override string ToString()
        {
            string str = "EventPram\n";
            return str;
        }
        public virtual bool AutoCombin()
        {
            return false;
        }
        public virtual bool Combin(EventParam cmd)
        {
            return false;
        }
    }

    public class EventParamCommon : EventParam
    {
        public static EventParamCommon New()
        {
            return new EventParamCommon();
        }
        Dictionary<string, int> m_DataInt;
        Dictionary<string, float> m_DataFloat;
        Dictionary<string, long> m_DataLong;
        Dictionary<string, Vector3> m_DataVector;
        Dictionary<string, string> m_DataString;
        Dictionary<string, UnityEngine.Object> m_ObjList;
        Dictionary<string, List<UnityEngine.Object>> m_ObjListDic;
        Dictionary<string, ICommonObject> m_CommonObjDic;
        Dictionary<string, Action> m_ActionDic;





        #region 赋值操作

        public void AddDataString(string key, string value)
        {
            if (m_DataString == null)
                m_DataString = new Dictionary<string, string>();
            if (m_DataString.ContainsKey(key))
            {
                m_DataString.Remove(key);
            }
            m_DataString.Add(key, value);
        }

        public void AddDataInt(string key, int value)
        {
            if (m_DataInt == null)
                m_DataInt = new Dictionary<string, int>();
            if (m_DataInt.ContainsKey(key))
                m_DataInt.Remove(key);
            m_DataInt.Add(key, value);
        }
        public void AddDataFloat(string key, float value)
        {
            if (m_DataFloat == null)
                m_DataFloat = new Dictionary<string, float>();
            if (m_DataFloat.ContainsKey(key))
                m_DataFloat.Remove(key);
            m_DataFloat.Add(key, value);

        }
        public void AddDataLong(string key, long value)
        {
            if (m_DataLong == null)
                m_DataLong = new Dictionary<string, long>();
            if (m_DataLong.ContainsKey(key))
                m_DataLong.Remove(key);
            m_DataLong.Add(key, value);
        }

        public void AddDataObject(string key, UnityEngine.Object obj)
        {
            if (m_ObjList == null)
                m_ObjList = new Dictionary<string, UnityEngine.Object>();
            if (m_ObjList.ContainsKey(key))
                m_ObjList.Remove(key);
            m_ObjList.Add(key, obj);
        }
        public void AddDataCommonObject(string key, ICommonObject obj)
        {
            if (m_CommonObjDic == null)
                m_CommonObjDic = new Dictionary<string, ICommonObject>();
            m_CommonObjDic.Add(key, obj);
        }


        public void AddDataVector(string key, Vector3 obj)
        {
            if (m_DataVector == null)
                m_DataVector = new Dictionary<string, Vector3>();
            m_DataVector.Add(key, obj);
        }

        public void AddDataAction(string key, Action action)
        {
            if (m_ActionDic == null)
                m_ActionDic = new Dictionary<string, Action>();
            m_ActionDic.Add(key, action);
        }
        public void AddDataObjList(string key, List<UnityEngine.Object> list)
        {
            if (m_ObjListDic == null)
                m_ObjListDic = new Dictionary<string, List<UnityEngine.Object>>();
            if (m_ObjListDic.ContainsKey(key))
            {
                m_ObjListDic.Remove(key);
            }
            m_ObjListDic.Add(key, list);
        }
        #endregion
        #region 取值操作

        public string GetDataString(string key)
        {
            return GetData<string>(m_DataString, key);
        }
        public bool IsHaveString(string key)
        {
            return IsHaveData<string>(m_DataString, key);
        }
        public int GetDataInt(string key)
        {
            return GetData<int>(m_DataInt, key);
        }
        public bool IsHaveInt(string key)
        {
            return IsHaveData<int>(m_DataInt, key);
        }
        public float GetDataFloat(string key)
        {
            return GetData<float>(m_DataFloat, key);
        }
        public bool IsHaveFloat(string key)
        {
            return IsHaveData<float>(m_DataFloat, key);
        }

        public long GetDataLong(string key)
        {
            return GetData<long>(m_DataLong, key);
        }
        public bool IsHaveLong(string key)
        {
            return IsHaveData<long>(m_DataLong, key);
        }
        public UnityEngine.Object GetDataObject(string key)
        {
            return GetData<UnityEngine.Object>(m_ObjList, key);
        }

        public Action GetDataAction(string key)
        {
            return GetData<Action>(m_ActionDic, key);
        }
        public bool IsHaveObject(string key)
        {
            return IsHaveData<UnityEngine.Object>(m_ObjList, key);
        }
        public bool IsHaveCommonObject(string key)
        {
            return IsHaveData<ICommonObject>(m_CommonObjDic, key);
        }
        public ICommonObject GetDataCommonObject(string key)
        {
            return GetData<ICommonObject>(m_CommonObjDic, key);
        }


        public Vector3 GetDataVector(string key)
        {
            return GetData<Vector3>(m_DataVector, key);
        }
        public bool IsHaveVector(string key)
        {
            return IsHaveData<Vector3>(m_DataVector, key);
        }

        public List<UnityEngine.Object> GetDataObjList(string key)
        {
            return GetData<List<UnityEngine.Object>>(m_ObjListDic, key);
        }
        public bool IsHaveObjList(string key)
        {
            return IsHaveData<List<UnityEngine.Object>>(m_ObjListDic, key);
        }

        bool IsHaveData<T>(Dictionary<string, T> dic, string key)
        {
            if (dic == null)
                return false;
            return dic.ContainsKey(key);
        }

        public bool IsHaveAction(string key)
        {
            return IsHaveData<Action>(m_ActionDic, key);
        }
        T GetData<T>(Dictionary<string, T> dic, string key)
        {
            T value;
            if (dic == null || !dic.TryGetValue(key, out value))
            {
                return default(T);
            }
            return value;
        }
        #endregion 

        public override EventParam Clone()
        {
            EventParamCommon data = new EventParamCommon();
            if (m_DataString != null)
                data.m_DataString = new Dictionary<string, string>(m_DataString);
            if (m_DataInt != null)
                data.m_DataInt = new Dictionary<string, int>(m_DataInt);
            if (m_DataFloat != null)
                data.m_DataFloat = new Dictionary<string, float>(m_DataFloat);
            if (m_DataLong != null)
                data.m_DataLong = new Dictionary<string, long>(m_DataLong);
            if (m_ObjList != null)
                data.m_ObjList = new Dictionary<string, UnityEngine.Object>(m_ObjList);
            if (m_DataVector != null)
                data.m_DataVector = new Dictionary<string, Vector3>(m_DataVector);

            return data;
        }

        //清除事件缓存
        public void Clear()
        {
            if (m_DataString != null)
                m_DataString.Clear();
            if (m_DataInt != null)
                m_DataInt.Clear();
            if (m_DataFloat != null)
                m_DataFloat.Clear();
            if (m_DataLong != null)
                m_DataLong.Clear();
            if (m_ObjList != null)
                m_ObjList.Clear();
            if (m_DataVector != null)
                m_DataVector.Clear();
        }

        public void RemoveFloat(string key)
        {
            if (m_DataFloat.ContainsKey(key))
            {
                m_DataFloat.Remove(key);
            }
        }

        public void RemoveString(string key)
        {
            if (m_DataString.ContainsKey(key))
            {
                m_DataString.Remove(key);
            }
        }

        public void CopyFromParam(EventParamCommon param)
        {
            if (param.m_DataInt != null)
            {
                foreach (var key in param.m_DataInt.Keys)
                {
                    AddDataInt(key, param.m_DataInt[key]);
                }
            }
            if (param.m_DataFloat != null)
            {
                foreach (var key in param.m_DataFloat.Keys)
                {
                    AddDataFloat(key, param.m_DataFloat[key]);
                }
            }
            if (param.m_DataLong != null)
            {
                foreach (var key in param.m_DataLong.Keys)
                {
                    AddDataLong(key, param.m_DataLong[key]);
                }
            }
            if (param.m_DataVector != null)
            {
                foreach (var key in param.m_DataVector.Keys)
                {
                    AddDataVector(key, param.m_DataVector[key]);
                }
            }
            if (param.m_DataString != null)
            {
                foreach (var key in param.m_DataString.Keys)
                {
                    AddDataString(key, param.m_DataString[key]);
                }
            }
            if (param.m_ObjList != null)
            {
                foreach (var key in param.m_ObjList.Keys)
                {
                    AddDataObject(key, param.m_ObjList[key]);
                }
            }
            if (param.m_ObjListDic != null)
            {
                foreach (var key in param.m_ObjListDic.Keys)
                {
                    AddDataObjList(key, param.m_ObjListDic[key]);
                }
            }

            if (param.m_CommonObjDic != null)
            {
                foreach (var key in param.m_CommonObjDic.Keys)
                {
                    AddDataCommonObject(key, param.m_CommonObjDic[key]);
                }
            }

        }

        enum ValueType
        {
            Int,
            Float,
            Vector,
            String,
            Long,
            Object,
        }
        public override string ToString()
        {
            string str = "EventParamCommon\n";
            if (m_DataString != null)
            {
                foreach (var item in m_DataString)
                {
                    str += "[" + item.Key + "]=" + item.Value + "\n";//
                }
            }
            if (m_DataInt != null)
            {
                foreach (var item in m_DataInt)
                {
                    str += "[" + item.Key + "]=" + item.Value + "\n";//
                }
            }
            if (m_DataFloat != null)
            {
                foreach (var item in m_DataFloat)
                {
                    str += "[" + item.Key + "]=" + item.Value + "\n";//
                }
            }
            if (m_DataLong != null)
            {
                foreach (var item in m_DataLong)
                {
                    str += "[" + item.Key + "]=" + item.Value + "\n";//
                }
            }
            if (m_ObjList != null)
            {
                foreach (var item in m_ObjList)
                {
                    str += "[" + item.Key + "]=" + item.Value.ToString() + "\n";//
                }
            }
            if (m_DataVector != null)
            {
                foreach (var item in m_DataVector)
                {
                    str += "[" + item.Key + "]=" + item.Value.ToString() + "\n";//
                }
            }
            return str;
        }


    }

    public class EventMgr
    {
        #region
        private static EventMgr m_instance;
        public static EventMgr Instance()
        {
            if (m_instance == null)
            {
                m_instance = new EventMgr();
            }
            return m_instance;
        }
        #endregion

        public delegate bool EventFunction(int ge, EventParam parameter);



        public struct EventNode
        {
            EventFunction function;

            public bool IsInvalid()
            {
                return function == null;
            }

            bool m_bFromLua;
            public EventNode(EventFunction fun, bool bFromLua)
            {
                function = fun;

                m_bFromLua = bFromLua;
            }



            public bool Trigger(int ge, EventParam parameter)
            {
                if (function != null)
                {
                    // 如果是从lua过来的，必须返回true，不能截断
                    if (m_bFromLua)
                    {
                        function(ge, parameter);
                        return true;
                    }
                    return function(ge, parameter);
                }

                return true;
            }

            /// <summary>
            /// 判断两个事件节点是否相等。
            /// </summary>
            /// <param name="node">要比较的事件节点。</param>
            /// <returns>在不为null的情况下，function或functionLua其中一个相等则为true。</returns>
            public bool IsEqual(EventNode node)
            {
                if (function != null)
                {
                    return function == node.function;
                }

                return node.function == null;
            }


        }


        private Dictionary<int, List<EventNode>> eventMap = new Dictionary<int, List<EventNode>>();


        // removeOnLoadScene决定此监听注册是否在加载场景时自动移除，大部分监听都应该选择自动移除，这是防止内存泄漏的保障机制

        public void AddListener(int ge, EventFunction ef, bool bFromLua = false)
        {
            AddListener(ge, new EventNode(ef, bFromLua));
        }

        private void AddListener(int ge, EventNode node)
        {
            // 如果第一次注册此类事件，需要创建对应链表
            List<EventNode> list;
            if (!eventMap.TryGetValue(ge, out list))
            {
                list = new List<EventNode>();
                eventMap.Add(ge, list);
            }

            // 如果重复注册则返回
            for (int i = 0; i < list.Count; ++i)
            {
                if (list[i].IsEqual(node))
                {
                    return;
                }
            }
            list.Add(node);
        }

        public void RemoveListener(int ge, EventFunction ef)
        {
            RemoveListener(ge, new EventNode(ef, false));
        }

        public void RemoveListener(int ge, EventNode node)
        {
            List<EventNode> list;
            if (!eventMap.TryGetValue(ge, out list))
            {
                return;
            }

            for (int i = 0; i < list.Count; ++i)
            {
                if (list[i].IsEqual(node))
                {
                    list.RemoveAt(i);
                    break;
                }
            }
        }


        public struct EventData
        {
            public EventData(int ge, EventParam ep)
            {
                gameevent = ge;
                if (ep != null)
                    parameter = ep.Clone();
                else
                    parameter = null;
                //if( typeof(parameter) != typeof(ep) )
            }

            public int gameevent;
            public EventParam parameter;
        }

        List<EventData> dataList = new List<EventData>();

        Dictionary<SmartTimer, EventData> m_DelayEvent = new Dictionary<SmartTimer, EventData>();
        public void DoUpdate()
        {
            for (int i = 0; i < dataList.Count; ++i)
            {
                EventData ed = dataList[i];
                TriggerEvent(ed.gameevent, ed.parameter);
            }
            dataList.Clear();

            foreach (var item in m_DelayEvent)
            {
                // add by 张辉 : 2021,12,22
                // 这里直接发现一个就删除一个，下一帧在删其他的，因为这个本身就不会很常见，其他方法会很费，就暂时这么用了，如果以后发现有大规模的延迟消息，就要把这里优化一下
                if (item.Key.DoUpdate())
                {
                    TriggerEvent(item.Value.gameevent, item.Value.parameter);
                    m_DelayEvent.Remove(item.Key);
                    break;
                }
            }
        }

        public void TriggerEvent(int ge, string key, int value)
        {
            EventParamCommon param = new EventParamCommon();
            param.AddDataInt(key, value);
            TriggerEvent(ge, param);
        }

        // 如果 fDelay == 0 说明是下一帧激发
        public void TriggerEventDelay(int ge, EventParam param, float fDelay = 0)
        {
            //如果是自动合并的消息，就要处理合并操作

            EventData data = new EventData(ge, param);
            if (fDelay <= 0)
            {
                if (param != null && param.AutoCombin())
                {
                    foreach (var item in dataList)
                    {
                        if (item.parameter.Combin(param))
                            return;
                    }
                }
                dataList.Add(data);
            }
            else
            {
                SmartTimer st = new SmartTimer();
                st.DoReset(fDelay);
                m_DelayEvent.Add(st, data);
            }
        }
        public void TriggerEvent(int ge, EventParam parameter)
        {
            //将事件响应队列复制一个副本，因为事件在响应的过场中有可能会改变响应列表
            List<EventNode> list;
            if (!eventMap.TryGetValue(ge, out list))
            {
                return;
            }
            for (int i = 0; i < list.Count;)
            {
                if (list[i].IsInvalid())
                {
                    list.RemoveAt(i);
                    continue;
                }
                ++i;
            }
            List<EventNode> tmpList = new List<EventNode>(list);
            for (int i = 0; i < tmpList.Count;)
            {
                if (tmpList[i].IsInvalid())
                {
                    tmpList.RemoveAt(i);
                    continue;
                }
                if (tmpList[i].Trigger(ge, parameter) == false)
                    return;
                ++i;
            }
        }
    }
}